/**
 * @file STL_MysqlConnection.cpp
 * @author Derek Liu (yjkhtddx@sina.com)
 * @brief 
 * @version 0.1
 * @date 2020-10-14
 * 
 * @copyright Copyright Sci-Tech Laboratories (c) 2020
 * 
 */

#include "STL_MysqlConnection.h"
#include "STL_MysqlConnPool.h"
#include <stdio.h>
#include <string>
#include <string.h>
#include "STLOG.h"
#include "STL_Json.h"

namespace STL
{
    void STL_MysqlConnection::charset(const std::string &cs)
    {
        std::string sql;
        sql.assign("set NAMES '").append(cs).append("'");
        mysql_real_query(conn, sql.c_str(), sql.size());
    }

    STL_MysqlConnection::STL_MysqlConnection(std::string host, unsigned int port, std::string user, std::string passwd, std::string database)
    {
        // 初始化连接
        conn = mysql_init(NULL);

        {
            // 禁用SSL
            // enum mysql_ssl_mode
            // {
            //     SSL_MODE_DISABLED = 1,//已停用
            //     SSL_MODE_PREFERRED,//首选
            //     SSL_MODE_REQUIRED,//必需的
            //     SSL_MODE_VERIFY_CA,//核实CA
            //     SSL_MODE_VERIFY_IDENTITY//核实身份
            // };
            unsigned int tempInt = 1;
            mysql_options(conn, MYSQL_OPT_SSL_MODE, &tempInt);
            // mysql_options(conn, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (const char *)&this->sslVerifyServer);
        }

#if MYSQL_VERSION_ID >= 50013
        {
            my_bool reconnect = true;
            mysql_options(conn, MYSQL_OPT_RECONNECT, &reconnect);
        }
#endif

        // if (!this->charset.empty())
        // {
        //     mysql_options(conn, MYSQL_SET_CHARSET_NAME, this->charset.c_str());
        // }

        // if (this->compress)
        // {
        //     mysql_options(conn, MYSQL_OPT_COMPRESS, (const char *)0);
        // }

        // if (!this->initCommand.empty())
        // {
        //     mysql_options(conn, MYSQL_INIT_COMMAND, this->initCommand.c_str());
        // }

        // if (this->readTimeout > 0)
        // {
        //     mysql_options(conn, MYSQL_OPT_READ_TIMEOUT, (const char *)&this->readTimeout);
        // }

        // if (this->timeout > 0)
        // {
        //     mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&this->timeout);
        // }

        // if (this->writeTimeout > 0)
        // {
        //     mysql_options(conn, MYSQL_OPT_WRITE_TIMEOUT, (const char *)&this->writeTimeout);
        // }

        // 执行物理的tcp连接动作,完成三次握手
        if (!mysql_real_connect(conn, host.c_str(), user.c_str(), passwd.c_str(), database.c_str(), port, NULL, 0))
        {
            printf("[database connecting Failed]> mysql -h%s -u%s -P%s -p%s %s\n", host.c_str(), user.c_str(), std::to_string(port).c_str(), passwd.c_str(), database.c_str());
            printf("xPool: Error connecting to database: %s\n", mysql_error(conn));
            exit(-1);
        }
        else
        {

            printf("[database connecting Succeeded]> mysql -h%s -u%s -P%s -p%s %s\n", host.c_str(), user.c_str(), std::to_string(port).c_str(), passwd.c_str(), database.c_str());
            charset("utf8");
        }
    }

    STL_MysqlConnection::~STL_MysqlConnection()
    {
        //mysql_thread_end();

        // 关闭TCP连接,四次挥手
        mysql_close(conn);
    }

    STL_MysqlQueryResult STL_MysqlConnection::executeQuery(std::string statement, long tid, bool log /* = true*/)
    {
        // 怎么感觉这个锁加上多连接池就没有意义了
        // std::lock_guard<std::mutex> guard(STL_MysqlConnPool::s_execMutex);

        const char *query = statement.c_str();

        unsigned int len = (unsigned int)strlen(query);

        char q[1024 * 5]; //TODO 需要添加数组越界检测或者修改其他方式
        strncpy(q, query, len);
        q[len] = 0;

        if (log)
        {
            stlog_trace("--------------------------------------------------\n");
            stlog_trace("%s\n", query);
            stlog_trace("--------------------------------------------------\n");
        }

        int status = 0;
        try
        {
            status = mysql_real_query(conn, q, len);
        }
        catch (const std::exception &e)
        {
            stlog_error("%s\n", e.what());
            status = -1;
        }

        if (status)
        {
            stlog_error("Error making query: %s\n", mysql_error(conn));
        }

        MYSQL_RES *resultSet;

        resultSet = mysql_store_result(conn);

        STL_MysqlQueryResult queryResult;
        queryResult.json_table = JSONCPP::Value(JSONCPP::arrayValue);

        queryResult.affected_rows = mysql_affected_rows(conn); //返回的行数改变/删除/插入的最后 UPDATE, DELETE或 INSERT查询。
        if (queryResult.affected_rows == ((my_ulonglong)-1))
            queryResult.affected_rows = 0;
        queryResult.error_number = mysql_errno(conn); //返回最近调用的MySQL函数的错误号。
        if (mysql_error(conn))
            queryResult.error_info = std::string(mysql_error(conn)); //返回有关最近调用的MySQL函数的错误消息。
        queryResult.insert_id = mysql_insert_id(conn);
        if (mysql_info(conn))
            queryResult.info = std::string(mysql_info(conn));
        if (mysql_sqlstate(conn))
            queryResult.sql_state = std::string(mysql_sqlstate(conn));
        queryResult.field_count = mysql_field_count(conn);
        if (!queryResult.error_number && queryResult.field_count != 0)
        {
            while (true)
            {
                MYSQL_FIELD *field;
                if (!(field = mysql_fetch_field(resultSet)))
                {
                    break;
                }
                queryResult.fields.push_back(std::string(field->name));
            }
            if (queryResult.fields.size() != queryResult.field_count)
            {
                printf("Error field count: %u,%lu\n", queryResult.field_count, queryResult.fields.size());
            }

            while (true)
            {
                MYSQL_ROW row;
                if (!(row = mysql_fetch_row(resultSet)))
                {
                    break;
                }
                std::vector<std::string> string_row;
                JSONCPP::Value tempValue(JSONCPP::ValueType::objectValue);
                for (int i = 0; i < (int)mysql_num_fields(resultSet); i++)
                {
                    char *temp = row[i];
                    std::string tempData = "";
                    if (temp)
                    {
                        tempData = std::string(temp);
                        tempValue[queryResult.fields[i]] = tempData;
                    }
                    else
                    {
                        tempValue[queryResult.fields[i]] = JSONCPP::Value(JSONCPP::ValueType::nullValue);
                    }
                    string_row.push_back(tempData);
                }
                if (string_row.size() != queryResult.field_count)
                {
                    printf("Error field count: %u,%lu\n", queryResult.field_count, string_row.size());
                }
                queryResult.json_table.append(tempValue);
                queryResult.string_table.push_back(string_row);
            }
        }

        mysql_free_result(resultSet); //free result after you get the result

        return queryResult;
    }
} // namespace STL
